//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<thread_debug.cpp>
///	@path	~/src/lib/system/
///	@date	2007/11/10
///	@desc	Thread debugging.

#include "config/config.h"

#include "lib/utilities/dict.h"
#include "lib/utilities/observer.h"
#include "lib/system/lock.h"
#include "lib/system/thread.h"
#include "lib/system/thread_debug.h"

#include <list>
#include <map>

namespace xeres {

#if (XERES_DEBUG_ENABLE_THREAD_SAFE_CHECK) && (!defined _PYXERES)

	namespace
	{
		// check category
		static inline bool _check_category( const WString& category )
		{
			for( size_t i = 0 , total = category.size() ; i < total ; ++i )
			{
				if( ( !isalpha( category[i] ) ) &&
					( category[i] != '*' ) &&
					( category[i] != '_' ) )
				{
					return false;
				}
			}
			return true;
		}

		// thread check manager
		class _ThreadCheckManager
		{
		public:

			// thread note
			struct _ThreadNote
			{
				// status
				int						m_affected;
				// single routine handle
				DWORD					m_single;
				// pool routine
				std::vector<DWORD> *	m_pool;

				/// \dtor
				~_ThreadNote( void )
				{
					if( m_pool )
						delete m_pool;
				}
			};
			
			// instance
			static _ThreadCheckManager& instance( void )
			{
				static _ThreadCheckManager s_ThreadCheckManagerInstance;
				return s_ThreadCheckManagerInstance;
			}

			/*!
				\brief
			*/
			void DefineThread( DWORD thread , const WString& category )
			{
				LockGuard<ThreadLock> locker( m_lock );

				assert( !category.empty() );
				assert( _check_category( category ) );

				// determine if rule has subscription tail
				wchar_t ending = category[category.size()-1];
				if( ending == '*' )
				{
					// * tailing to access pooling member
					WString pool = category.substr( 0 , category.size() - 1 );

					dict< WString , _ThreadNote * >::iterator it
						= m_threadSafeMap.find( pool );
					if( it == m_threadSafeMap.end() )
					{
						// make new
						_ThreadNote * note = new _ThreadNote;
						note->m_affected = 1;
						note->m_single = NULL;
						note->m_pool = new std::vector<DWORD>;
						note->m_pool->push_back( thread );
						m_threadSafeMap[ pool ] = note;
					}
					else
					{
						// push
						it->second->m_pool->push_back( thread );
					}
				}
				else
				{
					dict< WString , _ThreadNote * >::iterator it
						= m_threadSafeMap.find( category );
					if( it == m_threadSafeMap.end() )
					{
						// make new
						_ThreadNote * note = new _ThreadNote;
						note->m_affected = 1;
						note->m_single = thread;
						note->m_pool = NULL;
						m_threadSafeMap[ category ] = note;
					}
					else
					{
						TRACE_ERROR( _S("_ThreadCheckManager::DefineThread: Duplicate definition of '%s'.") ,
							category.c_str() );
						FATAL_ABORT( _S("Duplciate definition of thread checking category.") );
					}
				}
			}

			/*! 
				\brief
			*/
			const _ThreadNote * QueryNote( const WString& category )
			{
				LockGuard<ThreadLock> locker( m_lock );

				assert( !category.empty() );
				assert( _check_category( category ) );

				// determine if rule has subscription tail
				wchar_t ending = category[category.size()-1];
				if( ending == '*' )
				{
					// * tailing to access pooling member
					WString pool = category.substr( 0 , category.size() - 1 );

					dict< WString , _ThreadNote * >::iterator it
						= m_threadSafeMap.find( pool );
					if( it == m_threadSafeMap.end() )
					{
						// no matched
						return NULL;
					}
					_ThreadNote * note = it->second;
					if( note->m_pool == NULL )
					{
						TRACE_ERROR( _S("_ThreadCheckManager::MatchThread: '%s' is not a pool category.") ,
							pool.c_str() );
						FATAL_ABORT( _S("Thread check illegal usage.") );
					}
					return note;
				}
				else
				{
					// match simple
					dict< WString , _ThreadNote * >::iterator it
						= m_threadSafeMap.find( category );
					if( it == m_threadSafeMap.end() )
					{
						return NULL;
					}
					_ThreadNote * note = it->second;
					if( note->m_pool != NULL )
					{
						TRACE_ERROR( _S("_ThreadCheckManager::MatchThread: '%s' is not a single routine.") ,
							category.c_str() );
						FATAL_ABORT( _S("Thread check illegal usage.") );
					}
					return note;
				}
			}

			/*!
				\brief
			*/
			void PauseAffect( const WString& category , bool affected )
			{
				LockGuard<ThreadLock> locker( m_lock );

				assert( !category.empty() );
				assert( _check_category( category ) );

				// determine if rule has subscription tail
				wchar_t ending = category[category.size()-1];
				if( ending == '*' )
				{
					TRACE_ERROR( _S("_ThreadCheckManager::PauseAffect: '%s' is a pool category and cannot apply affection.") ,
						category.c_str() );
					FATAL_ABORT( _S("Thread check illegal usage.") );
				}
				else
				{
					// match simple
					dict< WString , _ThreadNote * >::iterator it
						= m_threadSafeMap.find( category );
					if( it == m_threadSafeMap.end() )
					{
						TRACE_ERROR( _S("_ThreadCheckManager::PauseAffect: '%s' is not defined.") ,
							category.c_str() );
						FATAL_ABORT( _S("Thread check illegal usage.") );
					}
					_ThreadNote * note = it->second;
					if( note->m_pool != NULL )
					{
						TRACE_ERROR( _S("_ThreadCheckManager::PauseAffect: '%s' is not a single routine.") ,
							category.c_str() );
						FATAL_ABORT( _S("Thread check illegal usage.") );
					}
					if( affected )
						note->m_affected = 1;
					else
						note->m_affected = 0;
				}
			}

			/*!
				\brief
			*/
			bool MatchThread( DWORD thread , const WString& category )
			{
				LockGuard<ThreadLock> locker( m_lock );

				assert( !category.empty() );
				assert( _check_category( category ) );

				// determine if rule has subscription tail
				wchar_t ending = category[category.size()-1];
				if( ending == '*' )
				{
					// * tailing to access pooling member
					WString pool = category.substr( 0 , category.size() - 1 );

					dict< WString , _ThreadNote * >::iterator it
						= m_threadSafeMap.find( pool );
					if( it == m_threadSafeMap.end() )
					{
						// no matched
						return false;
					}
					_ThreadNote * note = it->second;
					if( note->m_pool == NULL )
					{
						TRACE_ERROR( _S("_ThreadCheckManager::MatchThread: '%s' is not a pool category.") ,
							pool.c_str() );
						FATAL_ABORT( _S("Thread check illegal usage.") );
					}
					for( size_t i = 0 , total = note->m_pool->size() ; i < total ; ++i )
					{
						if( thread == (*note->m_pool)[i] )
						{
							return true;
						}
					}
					return false;
				}
				else
				{
					// match simple
					dict< WString , _ThreadNote * >::iterator it
						= m_threadSafeMap.find( category );
					if( it == m_threadSafeMap.end() )
					{
						return false;
					}
					_ThreadNote * note = it->second;
					if( note->m_pool != NULL )
					{
						TRACE_ERROR( _S("_ThreadCheckManager::MatchThread: '%s' is not a single routine.") ,
							category.c_str() );
						FATAL_ABORT( _S("Thread check illegal usage.") );
					}
					if( note->m_affected == 0 )
						return false;
					return note->m_single == thread;
				}
			}

			/*!
				\brief
			*/
			bool MatchThread( DWORD thread , const _ThreadNote * note )
			{
				LockGuard<ThreadLock> locker( m_lock );

				if( note->m_pool )
				{
					for( size_t i = 0 , total = note->m_pool->size() ; i < total ; ++i )
					{
						if( thread == (*note->m_pool)[i] )
						{
							return true;
						}
					}
					return false;
				}
				else
				{
					if( note->m_affected == 0 )
						return false;
					return note->m_single == thread;
				}
			}

			/// \brief	Reset all thread definition.
			void ResetAll( void )
			{
				LockGuard<ThreadLock> locker( m_lock );

				for( dict< WString , _ThreadNote * >::iterator it = m_threadSafeMap.begin() ;
					it != m_threadSafeMap.end() ; ++it )
				{
					delete it->second;
				}
				m_threadSafeMap.clear();
			}

		private:

			// @ctor
			_ThreadCheckManager( void )
			{
			}

			//@ dtor
			~_ThreadCheckManager( void )
			{
				ResetAll();
			}

		//@ data

			// lock
			ThreadLock						m_lock;

			// thread map
			dict< WString , _ThreadNote * >	m_threadSafeMap;
		};

		/*! 
			\class	_ThreadCheckRule
			\brief	Compiled rule structure to match rules fast.
		*/
		class _ThreadCheckRule
		{
		public:

			/// \ctor
			_ThreadCheckRule( const wchar_t * rules , rule_cache * ref )
				: m_ref( ref )
				, m_ruleDef( rules )
			{
				bool match_all = false;

				StringSeq tags;
				break_string( WString( rules ) , '|' , tags );
				for( size_t i = 0 , total = tags.size() ; i < total ; ++i )
				{
					bool not_in_this = tags[i][0] == '!';

					if( tags[i] == _S("*") )
					{
						match_all = true;
						continue;
					}

					WString cat = tags[i].substr( not_in_this ? 1 : 0 );

					RuleNote note;
					if( ( note.m_note = _ThreadCheckManager::instance().QueryNote( cat ) ) == NULL )
					{
						note.m_name = (wchar_t*)_wcsdup( cat.c_str() );
					}
					else
					{
						note.m_name = NULL;
					}
					note.m_invert = not_in_this;
					m_rules.push_back( note );
				}

				if( m_rules.size() == 0 )
				{
					XS_ASSERT( match_all );
				}
			}

			/// \dtor
			~_ThreadCheckRule( void )
			{
				for( size_t i = 0 , total = m_rules.size() ; i < total ; ++i )
				{
					if( m_rules[i].m_name )
						free( m_rules[i].m_name );
				}
				if( m_ref )
					m_ref->rule = NULL;
			}

			/// \brief	Check rules.
			bool CheckRule( const wchar_t * function )
			{
				if( m_rules.size() == 0 )
					return true;

				// get current thread handle
				DWORD thread = ::GetCurrentThreadId();

				// split them by ';'
				bool matched = false;

				for( size_t i = 0 , total = m_rules.size() ; i < total ; ++i )
				{
					RuleNote& rule = m_rules[i];
					bool not_in_this = rule.m_invert;

					if( rule.m_note == NULL )
					{
						// Try to match & cache note.
						if( ( rule.m_note = _ThreadCheckManager::instance().QueryNote( rule.m_name ) ) == NULL )
						{
							// Nothing
							if( not_in_this )
								// Invert, we don't want matched thread, so we pass.
								matched = true;
							else
								// Should take next check.
								continue;
						}
						else
						{
							// Matched, cached, then free name cache.
							free( rule.m_name );
							rule.m_name = NULL;
						}
					}

					// Check by cache note.
					if( rule.m_note && _ThreadCheckManager::instance().MatchThread( thread , rule.m_note ) )
					{
						// Found!
						if( not_in_this )
						{
							// But we don't want match this thread.
							TRACE_ERROR( _S("Thread safe check failed! '%s' is not running on expected thread rule(%s).") ,
								function , m_ruleDef );
							return false;
						}
						else
						{
							// OK!
							matched = true;
						}
					}
				}

				// Not matched yet.
				if( !matched )
				{
					TRACE_ERROR( _S("Thread safe check failed! '%s' is not running on expected thread rule(%s).") ,
						function , m_ruleDef );
					return false;
				}
				return true;
			}

			/// \brief	Get rule definition.
			const wchar_t * GetRuleDef( void ) const {
				return m_ruleDef;
			}

		private:

			typedef const _ThreadCheckManager::_ThreadNote * note_t;

			struct RuleNote
			{
				wchar_t *	m_name;
				note_t		m_note;
				bool		m_invert;
			};

			//@ data
			const wchar_t *				m_ruleDef;
			std::vector< RuleNote >		m_rules;
			rule_cache *				m_ref;
		};

		/*! 
			\class	_ThreadRulesManager
			\brief
		*/
		class _ThreadRulesManager
		{
		public:

			/// \instance
			static _ThreadRulesManager& instance( void )
			{
				static _ThreadRulesManager s_ThreadRulesManagerInstance;
				return s_ThreadRulesManagerInstance;
			}

			/// \brief Add rule to manager.
			_ThreadCheckRule * AddRule( const wchar_t * rules , rule_cache * ref )
			{
				std::map< const wchar_t * , _ThreadCheckRule * >::iterator it = m_rules.find( rules );
				if( it == m_rules.end() )
				{
					_ThreadCheckRule * rule = new _ThreadCheckRule( rules , ref );
					m_rules[ rules ] = rule;
					return rule;
				}
				return it->second;
			}

			/// \brief	Reset all rules.
			void ResetAll( void )
			{
				for( std::map< const wchar_t * , _ThreadCheckRule * >::iterator it = m_rules.begin() ;
					it != m_rules.end() ; ++it )
				{
					delete it->second;
				}
				m_rules.clear();
			}

		private:

			/// \dtor
			~_ThreadRulesManager( void )
			{
				ResetAll();
			}

			std::map< const wchar_t * , _ThreadCheckRule * > m_rules;
		};
	}

	// thread_check_define
	void thread_check_define( const wchar_t * cat )
	{
		// get current thread handle
		DWORD thread = ::GetCurrentThreadId();
		_ThreadCheckManager::instance().DefineThread( thread , cat );
	}

	// thread_check_affect
	void thread_check_affect( const wchar_t * cat , bool affected )
	{
		_ThreadCheckManager::instance().PauseAffect( cat , affected );
	}

	// rule_cache
	rule_cache::rule_cache( const wchar_t * rules )
	{
		rule = _ThreadRulesManager::instance().AddRule( rules , this );
	}

	// check
	bool rule_cache::check( const wchar_t * function )
	{
		if( rule )
			return static_cast< _ThreadCheckRule * >( rule )->CheckRule( function );
		TRACE_FATAL( L"Failed of checking rules running on function %s." ,
			function );
		//FATAL_ABORT( L"Failed of checking thread rules." );
		return false;
	}
	// thread_check_reset_all
	void thread_check_reset_all( void )
	{
		_ThreadRulesManager::instance().ResetAll();
		_ThreadCheckManager::instance().ResetAll();
	}
#endif
} // namespace xeres
